UIKit開発者向けのFlutter
UIKitの使用経験のあるiOS開発者 Flutterを使ってモバイルアプリを書きたい人 このガイドを確認する必要があります。 既存の UIKit の知識を Flutter に適用する方法について説明します。
Flutter はクロスプラットフォーム アプリケーションを構築するためのフレームワークです Dart プログラミング言語を使用します。 Dart を使用したプログラミングのいくつかの違いを理解するには Swift を使用したプログラミングについては、を参照してください。Swift 開発者として Dart を学ぶとSwift 開発者向けの Flutter 同時実行性。
iOS および UIKit の知識と経験 Flutter を使用して構築する場合、これらは非常に価値があります。 Flutter は多くの調整も行います iOS 上で実行するときのアプリの動作に影響を与えます。 その方法については、を参照してください。プラットフォームの適応。
このドキュメントは、飛び回ってクックブックとして使用できます。 自分のニーズに最も関連する質問を見つけます。
概要
ビューとウィジェット
UIKit では、UI で作成するもののほとんどはビュー オブジェクトを使用して行われます。
の例ですUIView
クラス。
これらは他のもののコンテナとして機能できます。UIView
クラス、
レイアウトを形成します。
Flutter では、おおよそ次のものに相当します。UIView
ですWidget
。
ウィジェットは iOS ビューに正確にマッピングされません。
Flutter の仕組みに慣れていくうちに
これらは「UI を宣言して構築する方法」と考えることができます。
ただし、これらにはいくつかの違いがあります。UIView
。
まず、ウィジェットの有効期間は異なります。ウィジェットは不変です。
変更する必要があるまでのみ存在します。
ウィジェットまたはその状態が変化するたびに、
Flutter のフレームワークは、ウィジェット インスタンスの新しいツリーを作成します。
比較すると、UIKit ビューは変更されても再作成されません。
むしろ、それは一度描画される変更可能なエンティティです
を使用して無効化されるまで再描画しませんsetNeedsDisplay()
。
さらに、それとは異なり、UIView
, Flutterのウィジェットは軽量なので、
部分的にはそれらの不変性によるものです。
それらはビューそのものではないため、
直接何かを描いているわけではありませんが、
むしろ、UI とそのセマンティクスの説明です。
これらは内部で実際のビュー オブジェクトに「インフレート」されます。
flutterには以下が含まれます材料成分図書館。 これらは、マテリアル デザインのガイドライン。 マテリアル デザインは柔軟なデザイン システムですすべてのプラットフォーム向けに最適化、iOSを含む。
しかし、Flutter は柔軟で表現力が十分にあります あらゆるデザイン言語を実装します。 iOS では、クパチーノのウィジェット次のようなインターフェイスを生成しますAppleのiOSデザイン言語。
ウィジェットの更新
UIKit でビューを更新するには、ビューを直接変更します。 Flutter では、ウィジェットは不変であり、直接更新されません。 代わりに、ウィジェットの状態を操作する必要があります。
ここで、ステートフル ウィジェットとステートレス ウィジェットの概念が登場します。
が入ってきます。StatelessWidget
まさにその通りです
同様に、状態が関連付けられていないウィジェットです。
StatelessWidgets
ユーザー インターフェイスの一部である場合に便利です。
説明は初期設定以外には依存しません
ウィジェット内の情報。
たとえば、UIKit の場合、これはUIImageView
あなたのロゴをimage
。実行時にロゴが変化しない場合は、
使うStatelessWidget
flutterで。
受信したデータに基づいて動的にUIを変更したい場合
HTTP 呼び出しを行った後、StatefulWidget
。
HTTP 呼び出しが完了したら、Flutter フレームワークに通知します。
そのウィジェットのState
が更新されるため、UI を更新できます。
ステートレスとステートレスの重要な違い
ステートフルウィジェットとは、StatefulWidget
は持っていますState
物体
状態データを保存し、ツリーの再構築間でそれを引き継ぎます。
だから失われません。
迷った場合は、次のルールを思い出してください。
ウィジェットが外部で変更された場合build
方法
(実行時のユーザー操作などのため)、
それはステートフルです。
ウィジェットが一度構築されると決して変更されない場合、そのウィジェットはステートレスになります。
ただし、ウィジェットがステートフルであっても、それを含む親ウィジェットは
自身がそれらの変更に反応していない場合は、ステートレスのままである可能性があります
(または他の入力)。
次の例は、StatelessWidget
。
共通のStatelessWidget
それはText
ウィジェット。
の実装を見てみると、Text
ウィジェット、
サブクラスが見つかりますStatelessWidget
。
Text(
'I like Flutter!',
style: TextStyle(fontWeight: FontWeight.bold),
);
上記のコードを見ると、次のことに気づくかもしれません。Text
ウィジェット
明示的な状態は伴いません。渡されたものをレンダリングします
コンストラクターだけです。
しかし、「I Like Flutter」を動的に変化させたい場合はどうすればよいでしょうか。
たとえば、FloatingActionButton
?
これを実現するには、Text
のウィジェットStatefulWidget
と
ユーザーがボタンをクリックすると更新されます。
例えば:
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
const SampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Sample App',
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
const SampleAppPage({super.key});
@override
State<SampleAppPage> createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
// Default placeholder text
String textToShow = 'I Like Flutter';
void _updateText() {
setState(() {
// Update the text
textToShow = 'Flutter is Awesome!';
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Sample App')),
body: Center(child: Text(textToShow)),
floatingActionButton: FloatingActionButton(
onPressed: _updateText,
tooltip: 'Update Text',
child: const Icon(Icons.update),
),
);
}
}
ウィジェットのレイアウト
UIKit では、Storyboard ファイルを使用する場合があります。 ビューを整理し、制約を設定するには、 または、ビュー コントローラーでプログラム的に制約を設定することもできます。 Flutter では、ウィジェット ツリーを構成してコードでレイアウトを宣言します。
次の例は、パディング付きの単純なウィジェットを表示する方法を示しています。
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Sample App')),
body: Center(
child: CupertinoButton(
onPressed: () {},
padding: const EdgeInsets.only(left: 10, right: 10),
child: const Text('Hello'),
),
),
);
}
任意のウィジェットにパディングを追加できます。 これは、iOS の制約の機能を模倣します。
Flutter が提供するレイアウトを表示できます の中にウィジェットカタログ。
ウィジェットの削除
UIKit では、次のように呼び出します。addSubview()
親に関して、
またremoveFromSuperview()
子ビューで
子ビューを動的に追加または削除します。
Flutterではウィジェットは不変なので、
に直接相当するものはありませんaddSubview()
。
代わりに、関数を親に渡すことができます
ウィジェットを返し、その子の作成を制御します
ブール値フラグを使用します。
次の例は、2 つのウィジェットを切り替える方法を示しています。
ユーザーがクリックすると、FloatingActionButton
:
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
const SampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Sample App',
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
const SampleAppPage({super.key});
@override
State<SampleAppPage> createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
// Default value for toggle.
bool toggle = true;
void _toggle() {
setState(() {
toggle = !toggle;
});
}
Widget _getToggleChild() {
if (toggle) {
return const Text('Toggle One');
}
return CupertinoButton(
onPressed: () {},
child: const Text('Toggle Two'),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: Center(
child: _getToggleChild(),
),
floatingActionButton: FloatingActionButton(
onPressed: _toggle,
tooltip: 'Update Text',
child: const Icon(Icons.update),
),
);
}
}
アニメーション
UIKit では、次のメソッドを呼び出してアニメーションを作成します。animate(withDuration:animations:)
ビューのメソッド。
Flutterではアニメーションライブラリを使用します。
アニメーション化されたウィジェット内にウィジェットをラップします。
Flutter では、AnimationController
、これはAnimation<double>
アニメーションを一時停止、シーク、停止、反転できます。
それには、Ticker
vsync が発生したときに通知します
線形補間を生成します
実行中の各フレームの 0 と 1 の間。
次に、1 つ以上を作成しますAnimation
s をコントローラーに取り付けます。
たとえば、次のように使用できます。CurvedAnimation
補間された曲線に沿ってアニメーションを実装します。
この意味で、コントローラーは「マスター」ソースです。
アニメの進捗状況
そしてそのCurvedAnimation
曲線を計算します
これは、コントローラーのデフォルトの直線運動を置き換えます。
ウィジェットと同様に、Flutter のアニメーションは合成で動作します。
ウィジェット ツリーを構築するときに、Animation
アニメーションに
ウィジェットの不透明度などのプロパティFadeTransition
、
そしてコントローラーにアニメーションを開始するように指示します。
次の例は、FadeTransition
それか
を押すと、ウィジェットがロゴにフェードインします。FloatingActionButton
:
import 'package:flutter/material.dart';
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
const SampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Fade Demo',
home: MyFadeTest(title: 'Fade Demo'),
);
}
}
class MyFadeTest extends StatefulWidget {
const MyFadeTest({super.key, required this.title});
final String title;
@override
State<MyFadeTest> createState() => _MyFadeTest();
}
class _MyFadeTest extends State<MyFadeTest>
with SingleTickerProviderStateMixin {
late AnimationController controller;
late CurvedAnimation curve;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
);
curve = CurvedAnimation(
parent: controller,
curve: Curves.easeIn,
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: FadeTransition(
opacity: curve,
child: const FlutterLogo(size: 100),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
controller.forward();
},
tooltip: 'Fade',
child: const Icon(Icons.brush),
),
);
}
}
詳細については、を参照してください。アニメーションとモーションのウィジェット、 のアニメーションのチュートリアル、 そしてそのアニメーションの概要。
画面上に描画する
UIKitでは、次のように使用します。CoreGraphics
線や形を描くには
画面。 Flutter には、Canvas
クラス、
描画に役立つ他の 2 つのクラスもあります。CustomPaint
とCustomPainter
、
後者は、キャンバスに描画するアルゴリズムを実装します。
Flutter で署名ペインタを実装する方法を学ぶには、 コリンの答えを参照してくださいスタックオーバーフロー。
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(home: DemoApp()));
class DemoApp extends StatelessWidget {
const DemoApp({super.key});
@override
Widget build(BuildContext context) => const Scaffold(body: Signature());
}
class Signature extends StatefulWidget {
const Signature({super.key});
@override
State<Signature> createState() => SignatureState();
}
class SignatureState extends State<Signature> {
List<Offset?> _points = <Offset?>[];
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanUpdate: (details) {
setState(() {
RenderBox? referenceBox = context.findRenderObject() as RenderBox;
Offset localPosition =
referenceBox.globalToLocal(details.globalPosition);
_points = List.from(_points)..add(localPosition);
});
},
onPanEnd: (details) => _points.add(null),
child:
CustomPaint(
painter: SignaturePainter(_points),
size: Size.infinite,
),
);
}
}
class SignaturePainter extends CustomPainter {
SignaturePainter(this.points);
final List<Offset?> points;
@override
void paint(Canvas canvas, Size size) {
final Paint paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5;
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != null && points[i + 1] != null) {
canvas.drawLine(points[i]!, points[i + 1]!, paint);
}
}
}
@override
bool shouldRepaint(SignaturePainter oldDelegate) =>
oldDelegate.points != points;
}
ウィジェットの不透明度
UIKit では、すべてが.opacity
また.alpha
。
Flutter では、ほとんどの場合、次のことが必要です。
ウィジェットをラップするOpacity
これを実現するためのウィジェット。
カスタムウィジェット
UIKit では通常、サブクラス化します。UIView
、または既存のビューを使用します。
目的の動作を実現するメソッドをオーバーライドして実装します。
Flutter では、次のようにカスタム ウィジェットを構築します。作曲する小さいウィジェット
(拡張する代わりに)。
たとえば、どうやって構築しますかCustomButton
それはコンストラクターでラベルを受け取りますか?
を構成する CustomButton を作成します。ElevatedButton
ラベル付きで、
延長するのではなくElevatedButton
:
class CustomButton extends StatelessWidget {
const CustomButton(this.label, {super.key});
final String label;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {},
child: Text(label),
);
}
}
次に、使用しますCustomButton
、
他の Flutter ウィジェットを使用するのと同じように:
@override
Widget build(BuildContext context) {
return const Center(
child: CustomButton('Hello'),
);
}
ナビゲーション
ドキュメントのこのセクションではナビゲーションについて説明します アプリのページ間、プッシュとポップのメカニズムなど。
ページ間の移動
UIKit では、ビュー コントローラー間を移動するには、UINavigationController
ビューコントローラーのスタックを管理します
表示する。
Flutter にも同様の実装があります。
を使ってNavigator
とRoutes
。
あRoute
アプリの「画面」または「ページ」の抽象化です。
そしてNavigator
ですウィジェットルートを管理するものです。ルートはおおよそ次のようにマッピングされます。UIViewController
。ナビゲーターは iOS と同様の方法で動作します。UINavigationController
、それができるという点でpush()
とpop()
ビューに移動するか、ビューから戻るかに応じてルートを決定します。
ページ間を移動するには、いくつかのオプションがあります。
- を指定してください
Map
路線名のこと。 - ルートに直接移動します。
次の例では、Map.
void main() {
runApp(
CupertinoApp(
home: const MyAppHome(), // becomes the route named '/'
routes: <String, WidgetBuilder>{
'/a': (context) => const MyPage(title: 'page A'),
'/b': (context) => const MyPage(title: 'page B'),
'/c': (context) => const MyPage(title: 'page C'),
},
),
);
}
ルートに移動するにはpush
その名前をNavigator
。
Navigator.of(context).pushNamed('/b');
のNavigator
クラスは Flutter でルーティングを処理し、取得するために使用されます。
スタックにプッシュしたルートから返された結果。
これを行うのは、await
上にいるFuture
によって返されましたpush()
。
たとえば、location
ユーザーが自分のルートを選択できるようにする
場所を指定するには、次のことを実行できます。
Object? coordinates = await Navigator.of(context).pushNamed('/location');
そして、あなたの内側でlocation
ユーザーがルートを選択すると、
位置、pop()
結果を含むスタック:
Navigator.of(context).pop({'lat': 43.821757, 'long': -79.226392});
別のアプリに移動する
UIKit でユーザーを別のアプリケーションに送信するには、
特定の URL スキームを使用している場合。
システム レベルのアプリの場合、スキームはアプリによって異なります。
Flutter でこの機能を実装するには、
ネイティブ プラットフォーム統合を作成するか、既存のプラグイン、 そのようなurl_launcher
。
手動でポップバックする
電話をかけるSystemNavigator.pop()
ダーツコードから
次の iOS コードを呼び出します。
UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([viewController isKindOfClass:[UINavigationController class]]) {
[((UINavigationController*)viewController) popViewControllerAnimated:NO];
}
これで希望どおりに動作しない場合は、独自のものを作成できますプラットフォームチャンネル任意の iOS コードを呼び出します。
ローカリゼーションの処理
iOS とは異なり、Localizable.strings
ファイル、
Flutter には現在、文字列を処理するための専用システムがありません。
現時点でのベストプラクティスは、コピーテキストを宣言することです。
クラス内で静的フィールドとして保存し、そこからアクセスします。例えば:
class Strings {
static const String welcomeMessage = 'Welcome To Flutter';
}
次のようにして文字列にアクセスできます。
Text(Strings.welcomeMessage);
デフォルトでは、Flutter は文字列として米国英語のみをサポートします。
他の言語のサポートを追加する必要がある場合は、
を含むflutter_localizations
パッケージ。
ダーツを追加する必要がある場合もありますintl
日付/時刻のフォーマットなど、i10n 機構を使用するためのパッケージ。
dependencies:
flutter_localizations:
sdk: flutter
intl: '^0.17.0'
を使用するには、flutter_localizations
パッケージ、
を指定しますlocalizationsDelegates
とsupportedLocales
アプリのウィジェットで:
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
localizationsDelegates: <LocalizationsDelegate<dynamic>>[
// Add app-specific localization delegate[s] here
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: <Locale>[
Locale('en', 'US'), // English
Locale('he', 'IL'), // Hebrew
// ... other locales the app supports
],
);
}
}
デリゲートには実際のローカライズされた値が含まれています。
一方supportedLocales
アプリがサポートするロケールを定義します。
上記の例では、MaterialApp
、
したがって、それは両方を持っていますGlobalWidgetsLocalizations
ベースウィジェットのローカライズされた値の場合、
そしてMaterialWidgetsLocalizations
マテリアル ウィジェットのローカリゼーション用。
使用する場合WidgetsApp
あなたのアプリでは後者は必要ありません。
これら 2 つのデリゲートには「デフォルト」値が含まれていることに注意してください。
ただし、1 人以上の代理人を指定する必要があります
独自のアプリのローカライズ可能なコピーの場合、
それらもローカライズしたい場合。
初期化すると、WidgetsApp
(またMaterialApp
)
を作成しますLocalizations
あなたのためのウィジェット、
あなたが指定した代理人と一緒に。
デバイスの現在のロケールには常にアクセスできます
からLocalizations
現在のコンテキストからのウィジェット
(の形で)Locale
オブジェクト)、またはWindow.locale
。
ローカライズされたリソースにアクセスするには、Localizations.of()
方法
特定のデリゲートによって提供される特定のローカリゼーション クラスにアクセスします。
使用intl_translation
翻訳可能なコピーを抽出するためのパッケージ
にアーブファイルを翻訳してアプリにインポートし直す
一緒に使用するためintl
。
Flutter の国際化とローカリゼーションの詳細については、
を参照してください国際化ガイド、サンプルコードがあります
ありとなしintl
パッケージ。
依存関係の管理
iOS では、CocoaPods との依存関係を追加します。Podfile
。
Flutter は Dart のビルド システムと Pub パッケージ マネージャーを使用します
依存関係を処理するため。ツールは、
Android および iOS のネイティブ ラッパー アプリを
それぞれのビルド システム。
iOS フォルダーに Podfile がある間、
Flutter プロジェクト。ネイティブを追加する場合にのみこれを使用します。
プラットフォームごとの統合に必要な依存関係。
一般に、使用しますpubspec.yaml
Flutter で外部依存関係を宣言します。
Flutter の優れたパッケージを見つけるのに最適な場所は次のとおりです。パブ.dev。
ビューコントローラー
ドキュメントのこのセクションでは、同等のものについて説明します。 Flutter の ViewController の説明とリッスンする方法 ライフサイクルイベント。
Flutter の ViewController に相当
UIKit では、ViewController
ユーザーインターフェースの一部を表し、
最も一般的には画面またはセクションに使用されます。
これらを組み合わせて複雑なユーザー インターフェイスを構築します。
アプリケーションの UI の拡張に役立ちます。
Flutter では、この仕事はウィジェットに割り当てられます。
ナビゲーションセクションで述べたように、
Flutter の画面はウィジェットによって表されます。
「すべてがウィジェットだ!」
使うNavigator
異なる間を移動するRoute
s
さまざまな画面またはページを表す
あるいは、同じデータの状態やレンダリングが異なる場合もあります。
ライフサイクル イベントのリッスン
UIKit では、メソッドをオーバーライドできます。ViewController
ビュー自体のライフサイクル メソッドをキャプチャするには、
または、ライフサイクル コールバックをAppDelegate
。
Flutter にはどちらの概念もありませんが、代わりに次のことができます。
にフックしてライフサイクル イベントをリッスンする
のWidgetsBinding
観察者と聞く者
のdidChangeAppLifecycleState()
変更イベント。
監視可能なライフサイクル イベントは次のとおりです。
inactive
- アプリケーションは非アクティブな状態にあり、受信していません ユーザー入力。このイベントは iOS でのみ機能します。 Android には同等のイベントがないためです。
paused
- アプリケーションは現在ユーザーには表示されませんが、 ユーザー入力に応答せず、バックグラウンドで実行されています。
resumed
- アプリケーションは表示され、ユーザー入力に応答します。
suspending
- アプリケーションは一時停止されています。 iOS プラットフォームには同等のイベントがありません。
これらの状態の意味の詳細については、「」を参照してください。AppLifecycleState
ドキュメンテーション。
レイアウト
このセクションでは、Flutter のさまざまなレイアウトについて説明します そしてUIKitとの比較。
リストビューの表示
UIKit では、次のリストを表示できます。
どちらかUITableView
またはUICollectionView
。
Flutter では、ListView
。
UIKit では、これらのビューにはデリゲート メソッドがあります
行数を決定するため、
各インデックス パスのセルとセルのサイズ。
Flutter の不変ウィジェット パターンにより、
ウィジェットのリストをListView
、
Flutter は次のことを確認します。
スクロールは速くてスムーズです。
import 'package:flutter/material.dart';
void main() {
runApp(const SampleApp());
}
class SampleApp extends StatelessWidget {
const SampleApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Sample App',
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
const SampleAppPage({super.key});
@override
State<SampleAppPage> createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List<Widget> _getListData() {
final List<Widget> widgets = [];
for (int i = 0; i < 100; i++) {
widgets.add(Padding(
padding: const EdgeInsets.all(10),
child: Text('Row $i'),
));
}
return widgets;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: ListView(children: _getListData()),
);
}
}
クリックされた内容の検出
UIKit では、デリゲート メソッドを実装します。tableView:didSelectRowAtIndexPath:
。
Flutter では、渡されたウィジェットによって提供されるタッチ処理を使用します。
import 'dart:developer' as developer;
import 'package:flutter/material.dart';
void main() {
runApp(const SampleApp());
}
class SampleApp extends StatelessWidget {
const SampleApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Sample App',
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
const SampleAppPage({super.key});
@override
State<SampleAppPage> createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List<Widget> _getListData() {
List<Widget> widgets = [];
for (int i = 0; i < 100; i++) {
widgets.add(
GestureDetector(
onTap: () {
developer.log('row tapped');
},
child: Padding(
padding: const EdgeInsets.all(10),
child: Text('Row $i'),
),
),
);
}
return widgets;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: ListView(children: _getListData()),
);
}
}
ListView の動的更新
UIKit では、リスト ビューのデータを更新します。
を使用してテーブルまたはコレクション ビューに通知します。reloadData
方法。
Flutter 内でウィジェットのリストを更新すると、setState()
、
データが視覚的に変化していないことがすぐにわかります。
なぜなら、いつsetState()
と呼ばれます、
Flutter レンダリング エンジンはウィジェット ツリーを確認します。
何かが変わったかどうかを確認します。
それがあなたの手元に届いたら、ListView
を実行します。==
チェック、
そして、その2つがListView
も同じです。
何も変更されていないため、更新する必要はありません。
を更新する簡単な方法については、ListView
、
新しいを作成しますList
の中にsetState()
、
古いリストから新しいリストにデータをコピーします。
このアプローチはシンプルですが、
大規模なデータセットには推奨されません。
次の例に示すように。
import 'dart:developer' as developer;
import 'package:flutter/material.dart';
void main() {
runApp(const SampleApp());
}
class SampleApp extends StatelessWidget {
const SampleApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Sample App',
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
const SampleAppPage({super.key});
@override
State<SampleAppPage> createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List<Widget> widgets = <Widget>[];
@override
void initState() {
super.initState();
for (int i = 0; i < 100; i++) {
widgets.add(getRow(i));
}
}
Widget getRow(int i) {
return GestureDetector(
onTap: () {
setState(() {
widgets = List.from(widgets);
widgets.add(getRow(widgets.length));
developer.log('row $i');
});
},
child: Padding(
padding: const EdgeInsets.all(10),
child: Text('Row $i'),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: ListView(children: widgets),
);
}
}
推奨される、効率的な、
リストを作成する効果的な方法は、ListView.Builder
。
この方法は、動的なイベントがある場合に最適です。
リストまたは非常に大量のデータを含むリスト。
import 'dart:developer' as developer;
import 'package:flutter/material.dart';
void main() {
runApp(const SampleApp());
}
class SampleApp extends StatelessWidget {
const SampleApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Sample App',
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
const SampleAppPage({super.key});
@override
State<SampleAppPage> createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List<Widget> widgets = [];
@override
void initState() {
super.initState();
for (int i = 0; i < 100; i++) {
widgets.add(getRow(i));
}
}
Widget getRow(int i) {
return GestureDetector(
onTap: () {
setState(() {
widgets.add(getRow(widgets.length));
developer.log('row $i');
});
},
child: Padding(
padding: const EdgeInsets.all(10),
child: Text('Row $i'),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: ListView.builder(
itemCount: widgets.length,
itemBuilder: (context, position) {
return getRow(position);
},
),
);
}
}
を作成する代わりに、ListView
を作成します。ListView.builder
これは 2 つの重要なパラメータを取ります: リストの初期長、
とItemBuilder
関数。
のItemBuilder
関数は次のようなものですcellForItemAt
iOS テーブルまたはコレクション ビューのデリゲート メソッド、
ポジションを取得し、
その位置にレンダリングしたいセル。
最後に、しかし最も重要なことですが、onTap()
関数
リストはもう再作成されませんが、代わりに.add
それには。
スクロールビューの作成
UIKit では、ビューをScrollView
それか
必要に応じてユーザーがコンテンツをスクロールできるようにします。
Flutter でこれを行う最も簡単な方法は、ListView
ウィジェット。
これは両方の機能として機能します。ScrollView
そしてiOSTableView
、
ウィジェットを垂直形式でレイアウトできるためです。
@override
Widget build(BuildContext context) {
return ListView(
children: const <Widget>[
Text('Row One'),
Text('Row Two'),
Text('Row Three'),
Text('Row Four'),
],
);
}
Flutter でウィジェットをレイアウトする方法に関する詳細なドキュメントについては、 を参照してくださいレイアウトのチュートリアル。
ジェスチャー検出とタッチイベント処理
このセクションでは、ジェスチャを検出する方法について説明します Flutter でさまざまなイベントを処理します。 そしてUIKitとの比較。
クリックリスナーの追加
UIKit では、GestureRecognizer
ビューへ
クリックイベントを処理します。
Flutter では、タッチ リスナーを追加する方法が 2 つあります。
-
ウィジェットがイベント検出をサポートしている場合は、ウィジェットに関数を渡します。 そして関数内でイベントを処理します。たとえば、
ElevatedButton
ウィジェットにはonPressed
パラメータ:@override Widget build(BuildContext context) { return ElevatedButton( onPressed: () { developer.log('click'); }, child: const Text('Button'), ); }
-
ウィジェットがイベント検出をサポートしていない場合は、 GestureDetector でウィジェットをラップし、関数を渡します に
onTap
パラメータ。class SampleTapApp extends StatelessWidget { const SampleTapApp({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( onTap: () { developer.log('tap'); }, child: const FlutterLogo( size: 200, ), ), ), ); } }
他のジェスチャの処理
使用するGestureDetector
聞くことができます
次のような幅広いジェスチャに対応します。
-
タッピング
onTapDown
- タップの原因となる可能性のあるポインタが接触しました。 特定の場所の画面。
onTapUp
- タップをトリガーするポインタが接触を停止しました。 特定の場所の画面。
onTap
- タップが発生しました。
onTapCancel
- 以前にトリガーしたポインター
onTapDown
タップの原因にはなりません。
-
ダブルタップ
onDoubleTap
- ユーザーが画面の同じ場所を 2 回タップしました。 素早い連続。
-
長押し
onLongPress
- ポインターが画面に触れたままになっている 長時間同じ場所にいること。
-
垂直方向のドラッグ
onVerticalDragStart
- ポインタが画面に触れ、動き始める可能性があります。 垂直に移動します。
onVerticalDragUpdate
- 画面に接触しているポインタ さらに垂直方向に移動しました。
onVerticalDragEnd
- 以前に接触していたポインタ 画面と垂直方向の移動が接触しなくなりました 画面とともに特定の速度で移動していた 画面に接触しなくなったとき。
-
水平方向のドラッグ
onHorizontalDragStart
- ポインタが画面に接触し、開始する可能性があります 水平方向に移動します。
onHorizontalDragUpdate
- 画面に接触しているポインタ さらに水平方向に移動しました。
onHorizontalDragEnd
- 以前に接触していたポインタ 画面と水平方向の移動はなくなりました 画面との接触。
次の例は、GestureDetector
ダブルタップで Flutter ロゴを回転させます。
class SampleApp extends StatefulWidget {
const SampleApp({super.key});
@override
State<SampleApp> createState() => _SampleAppState();
}
class _SampleAppState extends State<SampleApp>
with SingleTickerProviderStateMixin {
late AnimationController controller;
late CurvedAnimation curve;
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 2000),
);
curve = CurvedAnimation(
parent: controller,
curve: Curves.easeIn,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onDoubleTap: () {
if (controller.isCompleted) {
controller.reverse();
} else {
controller.forward();
}
},
child: RotationTransition(
turns: curve,
child: const FlutterLogo(
size: 200,
),
),
),
),
);
}
}
テーマ、スタイル、メディア
Flutter アプリケーションはスタイル設定が簡単です。切り替えることができます 明るいテーマと暗いテーマの間で、 テキストと UI コンポーネントのスタイルを変更します。 もっと。このセクションでは、Flutter アプリのスタイル設定の側面について説明します そして、UIKit で同じことを行う方法を比較します。
テーマの使用
Flutter には、すぐに使える美しい実装が付属しています。 マテリアル デザインの多くのスタイリングと 通常行うテーマのニーズ。
アプリでマテリアル コンポーネントを最大限に活用するには、
最上位のウィジェットを宣言し、MaterialApp
、
アプリケーションへのエントリ ポイントとして。MaterialApp
数値をラップする便利なウィジェットです
アプリケーションに一般的に必要なウィジェットの一覧
マテリアルデザインを実装しています。
それは、WidgetsApp
マテリアル固有の機能を追加することによって。
しかし、Flutter は実装するのに十分な柔軟性と表現力があります。 あらゆるデザイン言語。 iOS では、クパチーノ図書館に準拠したインターフェイスを作成するには、ヒューマンインターフェースガイドライン。 これらのウィジェットの完全なセットについては、 を参照してくださいクパチーノのウィジェットギャラリー。
を使用することもできますWidgetsApp
アプリのウィジェットとして、
同じ機能の一部を提供しますが、
しかし、それほど裕福ではありませんMaterialApp
。
子コンポーネントの色とスタイルをカスタマイズするには、
渡すThemeData
に反対するMaterialApp
ウィジェット。
たとえば、以下のコードでは、
プライマリ スウォッチは青に設定され、ディバイダの色はグレーに設定されます。
import 'package:flutter/material.dart';
class SampleApp extends StatelessWidget {
const SampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
dividerColor: Colors.grey,
),
home: const SampleAppPage(),
);
}
}
カスタムフォントの使用
UIKit では、任意のものをインポートしますttf
フォント ファイルをプロジェクトに追加する
そして参照を作成しますinfo.plist
ファイル。
Flutterでフォントファイルをフォルダに置きます
そしてそれを参照してくださいpubspec.yaml
ファイル、
画像をインポートする方法と同様です。
fonts:
- family: MyCustomFont
fonts:
- asset: fonts/MyCustomFont.ttf
- style: italic
次にフォントをText
ウィジェット:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: const Center(
child: Text(
'This is a custom font text',
style: TextStyle(fontFamily: 'MyCustomFont'),
),
),
);
}
テキストのスタイル設定
フォントに加えて、他のスタイル要素もカスタマイズできます。Text
ウィジェット。
のスタイルパラメータText
ウィジェットはTextStyle
物体、
ここでは、次のような多くのパラメータをカスタマイズできます。
color
decoration
decorationColor
decorationStyle
fontFamily
fontSize
fontStyle
fontWeight
hashCode
height
inherit
letterSpacing
textBaseline
wordSpacing
アプリ内に画像をバンドルする
iOS は画像とアセットを別個のアイテムとして扱いますが、
Flutter アプリにはアセットのみがあります。リソースとは、
に置かれたImages.xcasset
iOSのフォルダー、
Flutter のアセットフォルダーに配置されます。
iOS と同様に、アセットは画像だけでなく、あらゆる種類のファイルです。
たとえば、JSON ファイルが次の場所にあるとします。my-assets
フォルダ:
my-assets/data.json
で資産を宣言します。pubspec.yaml
ファイル:
assets:
- my-assets/data.json
そして、次のコマンドを使用してコードからアクセスします。AssetBundle
:
import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;
Future<String> loadAsset() async {
return await rootBundle.loadString('my-assets/data.json');
}
画像の場合、Flutter は iOS のような単純な密度ベースの形式に従います。
画像アセットは次のとおりです。1.0x
、2.0x
、3.0x
、またはその他の乗数。
flutterズdevicePixelRatio
比率を表します
単一の論理ピクセル内の物理ピクセルの数。
アセットは任意のフォルダーにあります。
Flutter には事前定義されたフォルダー構造はありません。
資産を(場所とともに)宣言します。
のpubspec.yaml
ファイルを開くと、Flutter がそれらを取得します。
たとえば、という名前の画像を追加するには、my_icon.png
あなたのときめきに
プロジェクトの場合は、任意の名前のフォルダーに保存することもできます。images
。
ベース画像(1.0x)をimages
フォルダー、および
適切な比率乗数にちなんで名付けられたサブフォルダー内の他のバリアント:
images/my_icon.png // Base: 1.0x image
images/2.0x/my_icon.png // 2.0x image
images/3.0x/my_icon.png // 3.0x image
次に、これらのイメージをpubspec.yaml
ファイル:
assets:
- images/my_icon.png
次を使用して画像にアクセスできるようになりましたAssetImage
:
AssetImage('images/a_dot_burr.jpeg')
または直接Image
ウィジェット:
@override
Widget build(BuildContext context) {
return Image.asset('images/my_image.png');
}
詳細については、を参照してください。Flutter でのアセットと画像の追加。
フォーム入力
このセクションでは、Flutter でフォームを使用する方法について説明します。 そしてUIKitとの比較。
ユーザー入力の取得
Flutter が別の状態を持つ不変ウィジェットをどのように使用するかを考えると、 ユーザー入力がどのように画像に反映されるのか疑問に思われるかもしれません。 UIKit では通常、ウィジェットの現在の値をクエリします。 ユーザー入力を送信するとき、またはそれに対するアクションを送信するとき。 Flutter ではどのように機能するのでしょうか?
実際には、フォームは Flutter のすべてと同様に処理されます。
特殊なウィジェットを使用します。持っている場合は、TextField
またはTextFormField
を指定できます。TextEditingController
ユーザー入力を取得するには:
class _MyFormState extends State<MyForm> {
// Create a text controller and use it to retrieve the current value.
// of the TextField!
final myController = TextEditingController();
@override
void dispose() {
// Clean up the controller when disposing of the Widget.
myController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Retrieve Text Input')),
body: Padding(
padding: const EdgeInsets.all(16),
child: TextField(controller: myController),
),
floatingActionButton: FloatingActionButton(
// When the user presses the button, show an alert dialog with the
// text the user has typed into our text field.
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
// Retrieve the text the user has typed in using our
// TextEditingController.
content: Text(myController.text),
);
},
);
},
tooltip: 'Show me the value!',
child: const Icon(Icons.text_fields),
),
);
}
}
詳細と完全なコードのリストは、次の場所にあります。テキストフィールドの値を取得する、 から flutterクックブック。
テキストフィールドのプレースホルダー
Flutter では、「ヒント」またはプレースホルダー テキストを簡単に表示できます
フィールドにInputDecoration
物体
の装飾コンストラクターパラメータにText
ウィジェット:
Center(
child: TextField(
decoration: InputDecoration(hintText: 'This is a hint'),
),
)
検証エラーの表示
「ヒント」と同じように、InputDecoration
物体
の装飾コンストラクターにText
ウィジェット。
ただし、最初からエラーを表示することは望ましくありません。
代わりに、ユーザーが無効なデータを入力した場合、
状態を更新し、新しい値を渡しますInputDecoration
物体。
import 'package:flutter/material.dart';
void main() {
runApp(const SampleApp());
}
class SampleApp extends StatelessWidget {
const SampleApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Sample App',
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
const SampleAppPage({super.key});
@override
State<SampleAppPage> createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
String? _errorText;
bool isEmail(String em) {
String emailRegexp =
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|'
r'(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|'
r'(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regExp = RegExp(emailRegexp);
return regExp.hasMatch(em);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: Center(
child: TextField(
onSubmitted: (text) {
setState(() {
if (!isEmail(text)) {
_errorText = 'Error: This is not an email';
} else {
_errorText = null;
}
});
},
decoration: InputDecoration(
hintText: 'This is a hint',
errorText: _errorText,
),
),
),
);
}
}
スレッド化と非同期性
このセクションでは、Flutter と UIKitとの比較。
非同期コードの作成
Dart にはシングルスレッド実行モデルがあり、
のサポート付きIsolate
s
(別のスレッドで Dart コードを実行する方法)、
イベントループと非同期プログラミング。
あなたがスポーンしない限り、Isolate
、
Dart コードはメイン UI スレッドで実行され、
イベントループによって駆動されます。 Flutterのイベントループは
iOS のメイン ループに相当します。つまり、
のLooper
それはメインスレッドに接続されています。
Dart のシングルスレッド モデルは、あなたがそうであることを意味するものではありません
すべてをブロック操作として実行するために必要
これにより UI がフリーズします。その代わり、
Dart 言語が提供する非同期機能を使用します。
そのようなasync
/await
、非同期作業を実行します。
たとえば、ネットワーク コードを実行しても、
を使用してハングする UIasync
/await
そしてダーツにやらせる
重労働:
Future<void> loadData() async {
final Uri dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final http.Response response = await http.get(dataURL);
setState(() {
data = jsonDecode(response.body);
});
}
一度await
ネットワーク通話が完了し、
呼び出して UI を更新するsetState()
、
これにより、ウィジェットのサブツリーの再構築がトリガーされます。
そしてデータを更新します。
次の例では、データを非同期的にロードし、
で表示しますListView
:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const SampleApp());
}
class SampleApp extends StatelessWidget {
const SampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Sample App',
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
const SampleAppPage({super.key});
@override
State<SampleAppPage> createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List<Map<String, dynamic>> data = <Map<String, dynamic>>[];
@override
void initState() {
super.initState();
loadData();
}
Future<void> loadData() async {
final Uri dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final http.Response response = await http.get(dataURL);
setState(() {
data = jsonDecode(response.body);
});
}
Widget getRow(int index) {
return Padding(
padding: const EdgeInsets.all(10),
child: Text('Row ${data[index]['title']}'),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return getRow(index);
},
),
);
}
}
作業の詳細については、次のセクションを参照してください。 バックグラウンドでの動作と、Flutter が iOS とどのように異なるのかを説明します。
バックグラウンドスレッドへの移動
Flutterはシングルスレッドでイベントループを実行するため、
(Node.js のように)、心配する必要はありません。
スレッド管理またはバックグラウンド スレッドの生成。
I/O バウンドの作業を行っている場合は、
ディスクアクセスやネットワーク通話など、
そうすれば安全に使用できますasync
/await
これで完了です。
一方、大量の計算を行う必要がある場合は、
CPU をビジー状態に保つ作業がある場合は、CPU を別の場所に移動したいとします。Isolate
イベントループのブロックを避けるため。
I/O バウンドの作業の場合は、関数を次のように宣言します。async
関数、
とawait
関数内の長時間実行タスクの場合:
Future<void> loadData() async {
final Uri dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final http.Response response = await http.get(dataURL);
setState(() {
data = jsonDecode(response.body);
});
}
これは通常、ネットワークまたはデータベース呼び出しを行う方法です。 どちらも I/O 操作です。
ただし、処理している場合があります。
大量のデータがあり、UI がハングします。
Flutterでは、使用しますIsolate
を活用する
複数の CPU コアを使用して長時間実行するか、
計算負荷の高いタスク。
アイソレートは、共有しない個別の実行スレッドです。
メイン実行メモリ ヒープを持つ任意のメモリ。
これは、メインスレッドから変数にアクセスできないことを意味します。
または呼び出して UI を更新しますsetState()
。
アイソレートはその名の通り、記憶を共有できない
(たとえば、静的フィールドの形式で)。
次の例は、単純な分離で次のことを示しています。 UI を更新するためにメインスレッドにデータを共有する方法。
Future<void> loadData() async {
final ReceivePort receivePort = ReceivePort();
await Isolate.spawn(dataLoader, receivePort.sendPort);
// The 'echo' isolate sends its SendPort as the first message.
final SendPort sendPort = await receivePort.first as SendPort;
final List<Map<String, dynamic>> msg = await sendReceive(
sendPort,
'https://jsonplaceholder.typicode.com/posts',
);
setState(() {
data = msg;
});
}
// The entry point for the isolate.
static Future<void> dataLoader(SendPort sendPort) async {
// Open the ReceivePort for incoming messages.
final ReceivePort port = ReceivePort();
// Notify any other isolates what port this isolate listens to.
sendPort.send(port.sendPort);
await for (final dynamic msg in port) {
final String url = msg[0] as String;
final SendPort replyTo = msg[1] as SendPort;
final Uri dataURL = Uri.parse(url);
final http.Response response = await http.get(dataURL);
// Lots of JSON to parse
replyTo.send(jsonDecode(response.body) as List<Map<String, dynamic>>);
}
}
Future<List<Map<String, dynamic>>> sendReceive(SendPort port, String msg) {
final ReceivePort response = ReceivePort();
port.send(<dynamic>[msg, response.sendPort]);
return response.first as Future<List<Map<String, dynamic>>>;
}
ここ、dataLoader()
それはIsolate
それが流れ込む
独自の別個の実行スレッド。
分離では、より多くの CPU を使用する実行が可能になります
処理 (大きな JSON の解析など)、
または、計算量の多い数学を実行する場合、
暗号化や信号処理など。
以下の完全な例を実行できます。
import 'dart:async';
import 'dart:convert';
import 'dart:isolate';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const SampleApp());
}
class SampleApp extends StatelessWidget {
const SampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Sample App',
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
const SampleAppPage({super.key});
@override
State<SampleAppPage> createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List<Map<String, dynamic>> data = <Map<String, dynamic>>[];
@override
void initState() {
super.initState();
loadData();
}
bool get showLoadingDialog => data.isEmpty;
Future<void> loadData() async {
final ReceivePort receivePort = ReceivePort();
await Isolate.spawn(dataLoader, receivePort.sendPort);
// The 'echo' isolate sends its SendPort as the first message.
final SendPort sendPort = await receivePort.first as SendPort;
final List<Map<String, dynamic>> msg = await sendReceive(
sendPort,
'https://jsonplaceholder.typicode.com/posts',
);
setState(() {
data = msg;
});
}
// The entry point for the isolate.
static Future<void> dataLoader(SendPort sendPort) async {
// Open the ReceivePort for incoming messages.
final ReceivePort port = ReceivePort();
// Notify any other isolates what port this isolate listens to.
sendPort.send(port.sendPort);
await for (final dynamic msg in port) {
final String url = msg[0] as String;
final SendPort replyTo = msg[1] as SendPort;
final Uri dataURL = Uri.parse(url);
final http.Response response = await http.get(dataURL);
// Lots of JSON to parse
replyTo.send(jsonDecode(response.body) as List<Map<String, dynamic>>);
}
}
Future<List<Map<String, dynamic>>> sendReceive(SendPort port, String msg) {
final ReceivePort response = ReceivePort();
port.send(<dynamic>[msg, response.sendPort]);
return response.first as Future<List<Map<String, dynamic>>>;
}
Widget getBody() {
bool showLoadingDialog = data.isEmpty;
if (showLoadingDialog) {
return getProgressDialog();
} else {
return getListView();
}
}
Widget getProgressDialog() {
return const Center(child: CircularProgressIndicator());
}
ListView getListView() {
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, position) {
return getRow(position);
},
);
}
Widget getRow(int i) {
return Padding(
padding: const EdgeInsets.all(10),
child: Text("Row ${data[i]["title"]}"),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: getBody(),
);
}
}
ネットワークリクエストを行う
Flutter でネットワーク呼び出しを行うのは簡単です。
人気のあるものを使用するhttp
パッケージ。この要約は
通常ならできるネットワークの多くを切り離す
自分で実装すると、ネットワーク呼び出しが簡単になります。
追加するには、http
パッケージを依存関係として実行しますflutter pub add
:
$ flutter pub add http
ネットワーク通話を行うには、
電話await
でasync
関数http.get()
:
Future<void> loadData() async {
final Uri dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final http.Response response = await http.get(dataURL);
setState(() {
data = jsonDecode(response.body);
});
}
長時間実行されるタスクの進行状況を表示する
UIKit では通常、UIProgressView
バックグラウンドで長時間実行されるタスクを実行しているとき。
Flutter では、ProgressIndicator
ウィジェット。
プログラムで制御して進行状況を表示する
ブール値フラグを介してレンダリングされるとき。
長時間実行されるタスクが開始される前に状態を更新するように Flutter に指示します。
そして終了後は非表示にします。
以下の例では、ビルド関数が 3 つの異なる関数に分かれています。
機能。もしもshowLoadingDialog
はtrue
(いつwidgets.length == 0
)、レンダリングします。ProgressIndicator
。
それ以外の場合は、レンダリングListView
ネットワーク呼び出しから返されたデータを使用します。
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const SampleApp());
}
class SampleApp extends StatelessWidget {
const SampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Sample App',
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
const SampleAppPage({super.key});
@override
State<SampleAppPage> createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List<Map<String, dynamic>> data = <Map<String, dynamic>>[];
@override
void initState() {
super.initState();
loadData();
}
bool get showLoadingDialog => data.isEmpty;
Future<void> loadData() async {
final Uri dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final http.Response response = await http.get(dataURL);
setState(() {
data = jsonDecode(response.body);
});
}
Widget getBody() {
if (showLoadingDialog) {
return getProgressDialog();
}
return getListView();
}
Widget getProgressDialog() {
return const Center(child: CircularProgressIndicator());
}
ListView getListView() {
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return getRow(index);
},
);
}
Widget getRow(int i) {
return Padding(
padding: const EdgeInsets.all(10),
child: Text("Row ${data[i]["title"]}"),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample App'),
),
body: getBody(),
);
}
}